#include "sign.srpc.h"
#include <signal.h>
#include "CryptoUtil.h"
#include <nlohmann/json.hpp>
#include <map>
#include <string>
#include <workflow/MySQLResult.h>
#include <wfrest/HttpServer.h>
#include <workflow/WFFacilities.h>
#include <iostream>
using namespace wfrest;
using namespace srpc;
using namespace protocol;
using JSON = nlohmann::json;

using std::map;
using std::string;

static WFFacilities::WaitGroup wait_group(1);

struct SignContext{
	SignResponse *resp;
    string username;
    string password;
};

void sig_handler(int)
{
    wait_group.done();
}

class SignServiceImpl : public Sign::Service
{
public:

    void signIn(SignRequest *request, SignResponse *response, srpc::RPCContext *ctx) override
    {
        SeriesWork * series = ctx->get_series();
        string username =request->username();
        string password =request->password();

        string sql = "select * from user_table where username = '"+username+"';";
        WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,&SignServiceImpl::siginMysqlCallback);
        mysqltask->get_req()->set_query(sql);

        SignContext * context = new SignContext{response,username,password};
        series->set_context(context);
        series->set_callback([context](const SeriesWork *){
                             delete context;
                             });

        series->push_back(mysqltask);
    }

    void signUp(SignRequest *request, SignResponse *response, srpc::RPCContext *ctx) override
    {
        SeriesWork * series = ctx->get_series();
        string username =request->username();
        string password =request->password();
        
        #ifdef DEBUG
            std::cout<<username<<"\n";
            std::cout<<password<<"\n";
        #endif

        string salt = CryptoUtil::generate_salt();
        string encode_password = CryptoUtil::hash_password(password,salt);
        string sql = "insert into user_table (username , password, salt) values ('"+username+"','"+encode_password +"','"+salt+"');";
        WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,&SignServiceImpl::doRegisterCallback);
        mysqltask->get_req()->set_query(sql);
        
        SignContext * context = new SignContext{response,username,password};
        series->set_context(context);
        series->set_callback([context](const SeriesWork *){
            delete context;
        });
        
        series->push_back(mysqltask);
    }

private:
    static void siginMysqlCallback(WFMySQLTask * mysqlTask){
        SignContext * context = static_cast<SignContext*>(series_of(mysqlTask)->get_context());
        if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
            context->resp->set_success(0);
            return;
        }

        MySQLResultCursor cursor(mysqlTask->get_resp());

        if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
            context->resp->set_success(0);
            return ;
        }

        if(cursor.get_cursor_status()==MYSQL_STATUS_GET_RESULT){
            map<string,MySQLCell> row;
            cursor.fetch_row(row);
            if(row.size()==0){
                context->resp->set_success(0);
                return;
            }
            string password = context->password;
            string salt = row["salt"].as_string();
            string encode_password = CryptoUtil::hash_password(password,salt);

            string storage_password = row["password"].as_string();

#ifdef DEBUG
            std::cout<<"1111"<<"\n";
#endif

            if(encode_password == storage_password){
                context->resp->set_success(1);
                context->resp->set_username(context->username);
                context->resp->set_token(CryptoUtil::generate_token(context->username));
#ifdef DEBUG
                std::cout<<context->resp->token()<<"\n";
#endif
            }else{
                context->resp->set_success(0);      
            }
        }
        return ;
    }
    
static void doRegisterCallback(WFMySQLTask * mysqlTask){
        SignContext * context = static_cast<SignContext*>(series_of(mysqlTask)->get_context());
    if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
        context->resp->set_success(0); 
        return;
    }

    MySQLResultCursor cursor(mysqlTask->get_resp());

    if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
        context->resp->set_success(0); 
        return ;
    }

    if(cursor.get_cursor_status()==MYSQL_STATUS_OK){
        context->resp->set_success(1);
    }
    return ;
    }

};

int main()
{
    signal(SIGINT,sig_handler); 
    GOOGLE_PROTOBUF_VERIFY_VERSION;
    unsigned short port = 1412;
    SRPCServer server;

    SignServiceImpl sign_impl;
    server.add_service(&sign_impl);

    if(server.start(port) == 0){
        wait_group.wait();
        server.stop();
        google::protobuf::ShutdownProtobufLibrary();
    }else{
        std::cerr << "Error: start signserver failed!\n";
        std::exit(1);
    }

    return 0;
}
